<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="r9gl4" id="r9gl4"><span data-lake-id="ub6acf0bb" id="ub6acf0bb">典型回答</span></h1>
  <p data-lake-id="udecb4181" id="udecb4181"><br></p>
  <p data-lake-id="u5fa56266" id="u5fa56266"><span data-lake-id="ued03bb2c" id="ued03bb2c">我们都知道String是不可变的，但是它是怎么实现的呢？</span></p>
  <p data-lake-id="ue4bc36de" id="ue4bc36de"><span data-lake-id="uf561bd3b" id="uf561bd3b">​</span><br></p>
  <p data-lake-id="ua7ca7c7f" id="ua7ca7c7f"><span data-lake-id="u9d78ce01" id="u9d78ce01">先来看一段String的源码（JDK 1.8）：</span></p>
  <p data-lake-id="u4f87324b" id="u4f87324b"><span data-lake-id="u15a4af25" id="u15a4af25">​</span><br></p>
  <pre lang="java"><code>
public final class String
    implements java.io.Serializable, Comparable&lt;String&gt;, CharSequence {

    /** The value is used for character storage. */
    private final char value[];

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

    public String substring(int beginIndex) {
        if (beginIndex &lt; 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        int subLen = value.length - beginIndex;
        if (subLen &lt; 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
    }

    public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }
}
</code></pre>
  <p data-lake-id="u0acf0597" id="u0acf0597"><span data-lake-id="u743e2b31" id="u743e2b31">​</span><br></p>
  <p data-lake-id="u3fa82ee8" id="u3fa82ee8"><span data-lake-id="u15f86718" id="u15f86718">以上代码，其实就包含了String不可变的主要实现了。</span></p>
  <p data-lake-id="ub9606b13" id="ub9606b13"><span data-lake-id="u011a586f" id="u011a586f">​</span><br></p>
  <ol list="u01eb749c">
   <li fid="ue61d2904" data-lake-id="uf56178c2" id="uf56178c2" data-lake-index-type="true"><strong><span data-lake-id="ub1a037df" id="ub1a037df">String类被声明为final，这意味着它不能被继承。那么他里面的方法就是没办法被覆盖的。</span></strong></li>
   <li fid="ue61d2904" data-lake-id="ue81ff549" id="ue81ff549" data-lake-index-type="true"><strong><span data-lake-id="u316425e6" id="u316425e6">用final修饰字符串内容的char[]（从JDK 1.9开始，char[]变成了byte[]），由于该数组被声明为final，一旦数组被初始化，就不能再指向其他数组。</span></strong></li>
   <li fid="ue61d2904" data-lake-id="u53d65a08" id="u53d65a08" data-lake-index-type="true"><strong><span data-lake-id="ufecd114c" id="ufecd114c">String类没有提供用于修改字符串内容的公共方法。例如，没有提供用于追加、删除或修改字符的方法。如果需要对字符串进行修改，会创建一个新的String对象。</span></strong></li>
  </ol>
  <p data-lake-id="u7fb4ad23" id="u7fb4ad23"><span data-lake-id="u32bf174c" id="u32bf174c">​</span><br></p>
  <p data-lake-id="ue20dc02f" id="ue20dc02f"><span data-lake-id="uc3c7bcac" id="uc3c7bcac">​</span><br></p>
  <p data-lake-id="ub0fdf859" id="ub0fdf859"><span data-lake-id="u7fdbc9e5" id="u7fdbc9e5">​</span><br></p>
  <p data-lake-id="ub5e6c48b" id="ub5e6c48b"><span data-lake-id="u450904a9" id="u450904a9">再然后，在他的一些方法中，如substring、concat等，在代码中如果有涉及到字符串的修改，也是通过new String()的方式新建了一个字符串。</span></p>
  <p data-lake-id="ub7fc8a70" id="ub7fc8a70"><span data-lake-id="u98184e1b" id="u98184e1b">​</span><br></p>
  <p data-lake-id="u89379c0f" id="u89379c0f"><span data-lake-id="u27cd671f" id="u27cd671f">所以，通过以上方式，使得一个字符串的内容，一旦被创建出来，就是不可以修改的了。</span></p>
  <p data-lake-id="u900d4914" id="u900d4914"><br></p>
  <blockquote data-lake-id="u6ead378b" id="u6ead378b">
   <p data-lake-id="ufeef3543" id="ufeef3543"><span data-lake-id="u2900bcc4" id="u2900bcc4">不可变对象是在完全创建后其内部状态保持不变的对象。这意味着，一旦对象被赋值给变量，我们既不能更新引用，也不能通过任何方式改变内部状态。</span></p>
  </blockquote>
  <p data-lake-id="ua41dc7a8" id="ua41dc7a8"><br></p>
  <p data-lake-id="u708759ea" id="u708759ea"><span data-lake-id="u1dd72cd4" id="u1dd72cd4">可是有人会有疑惑，String为什么不可变，我的代码中经常改变String的值啊，如下：</span></p>
  <p data-lake-id="u8f5cbcbc" id="u8f5cbcbc"><br></p>
  <pre lang="java"><code>
String s = "abcd";
s = s.concat("ef");
</code></pre>
  <p data-lake-id="u6d791cc7" id="u6d791cc7"><br></p>
  <p data-lake-id="u206870ac" id="u206870ac"><span data-lake-id="u84eb28fe" id="u84eb28fe">这样，操作，不就将原本的"abcd"的字符串改变成"abcdef"了么？</span></p>
  <p data-lake-id="u36089d1b" id="u36089d1b"><br></p>
  <p data-lake-id="u071aec6a" id="u071aec6a"><span data-lake-id="uff388b34" id="uff388b34">但是，虽然字符串内容看上去从"abcd"变成了"abcdef"，但是实际上，我们得到的已经是一个新的字符串了。</span></p>
  <p data-lake-id="u4bf6f49e" id="u4bf6f49e"><br></p>
  <p data-lake-id="u5287598d" id="u5287598d"><img src="https://cdn.nlark.com/yuque/0/2023/jpeg/5378072/1693569145559-1464948e-b069-4234-8f03-40dba93f044b.jpeg?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_19%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"><span data-lake-id="u5b1d755b" id="u5b1d755b">​</span></p>
  <p data-lake-id="u45997826" id="u45997826"><br></p>
  <p data-lake-id="uba19e522" id="uba19e522"><span data-lake-id="uf57f8112" id="uf57f8112">如上图，在堆中重新创建了一个"abcdef"字符串，和"abcd"并不是同一个对象。</span></p>
  <p data-lake-id="ud996f3dd" id="ud996f3dd"><br></p>
  <p data-lake-id="u562054a6" id="u562054a6"><span data-lake-id="u73335aa7" id="u73335aa7">所以，一旦一个string对象在内存(堆)中被创建出来，他就无法被修改。而且，String类的所有方法都没有改变字符串本身的值，都是返回了一个新的对象。</span></p>
  <p data-lake-id="u87512e09" id="u87512e09"><br></p>
  <p data-lake-id="u109ec6f0" id="u109ec6f0"><span data-lake-id="ucec21273" id="ucec21273">如果我们想要一个可修改的字符串，可以选择StringBuffer 或者 StringBuilder这两个代替String。</span></p>
 </body>
</html>